﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using ZoneFiveSoftware.Common.Visuals;
using ZoneFiveSoftware.Common.Visuals.Fitness;
using ZoneFiveSoftware.Common.Visuals.Util;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Data.Measurement;

using SportTracksSwimSetPlugin;

using SportTracksSwimSetPlugin.Common;

namespace SportTracksSwimSetPlugin.Edit
{
    class SwimSetWorker
    {
        public SwimSetWorker(IActivity activity)
        {
            this.swimActivity = activity;
            Log.WriteToLogfile("New Activity", Log.LogLevel.INFO);
        }

        public void addSwimDistance()
        {


            if (swimActivity.Laps == null)
            {
                // nothing to update; we have no laps
                return;
            }

            double totalDistanceMeters = 0;

            // this should be configurable by the user
            double perHundredTime = SwimSetSettings.PerHundredPaceSeconds;
            Log.WriteToLogfile("perHundredTime=" + perHundredTime.ToString(), Log.LogLevel.DEBUG);
            //int leastCommonLength = SwimSetSettings.leastCommonLength;
            int leastCommonLength = 25;
            Log.WriteToLogfile("leastCommonLength=" + leastCommonLength.ToString(), Log.LogLevel.DEBUG);
            
            // The data is transferred from the watch and stored in lap data after import
            // This will not allow the "stopped time" to be used, nor will the pace graph
            // be generated. To get these, we need to add distance tracks for each lap
            // and rest interval
            //
            // This could be a user setting, but at this time, I don't see the downside
            // of adding them all the time
            bool addDistanceTrack = true;

            // clear the previous distance track since we will
            // rebuild them from the lap data
            if (swimActivity.DistanceMetersTrack != null)
            {
                swimActivity.DistanceMetersTrack.Clear();
            }

            // if we add distance tracks, the stopped time will 
            // be auto-calculated and the user can decide to use
            // swim time or total time based on "include stopped time"
            swimActivity.DistanceMetersTrack = new DistanceDataTrack();

            // start by adding a distance track of 0 meters at the start of lap 0
            // otherwise, the first lap will be lost
            swimActivity.DistanceMetersTrack.Add(swimActivity.Laps[0].StartTime, 0);
            Log.WriteToLogfile("Added: " +
                swimActivity.DistanceMetersTrack[swimActivity.DistanceMetersTrack.Count - 1].ToString(), Log.LogLevel.DEBUG);

            // get the units for this activity
            Length.Units units = swimActivity.Category.DistanceUnits;

            Log.WriteToLogfile("Units: " + units.ToString(), Log.LogLevel.DEBUG);
            Log.WriteToLogfile("Num Laps: " + swimActivity.Laps.Count.ToString(), Log.LogLevel.DEBUG);

            // set the distance for each Active Lap based on lap time
            for (int lapNum = 0; lapNum < swimActivity.Laps.Count; lapNum++)
            {
                Log.WriteToLogfile("Lap " + lapNum.ToString(), Log.LogLevel.DEBUG);
                ILapInfo lap = swimActivity.Laps[lapNum];

                // even numbered laps are rest
                if ((lapNum % 2) == 1)
                {
                    lap.Rest = true;
                    lap.TotalDistanceMeters = 0;
                }
                else
                {
                    // this is the swim lap

                    // do not overwrite the distance if it already exists
                    // this way, the user can choose to edit the lap manually 
                    // and have it "stick"
                    //if (lap.TotalDistanceMeters == 0 && lap.TotalTime.TotalSeconds > 0)
                    if (lap.TotalDistanceMeters == 0)
                    {
                        lap.Rest = false;

                        // from the duration of lap, calculate distance swam
                        double lapDuration = lap.TotalTime.TotalSeconds;
                        double numHundreds = (lapDuration / perHundredTime);

                        // adjust the perHundred time such that 3" are added each time
                        // the distance is doubled after 100
                        //perHundredTime = SwimSetSettings.PerHundredPaceSeconds + (2 * (Math.Log(numHundreds,2) ));

                        // break it down to the number of lengths swam, then
                        // we can get the distance rouded per the pool length setting
                        Log.WriteToLogfile("Lap Duration=" + lapDuration.ToString(), Log.LogLevel.DEBUG);

                        double leastCommonLengthTime = perHundredTime * ( (double) leastCommonLength / 100);
                        Log.WriteToLogfile("leastCommonLengthTime=" + leastCommonLengthTime.ToString(), Log.LogLevel.DEBUG);

                        Decimal numLengths = Decimal.Round((decimal)(lapDuration / leastCommonLengthTime));
                        Log.WriteToLogfile("numLengths=" + numLengths.ToString(), Log.LogLevel.DEBUG);

                        // if the time doesn't even match one "length", set the distance as half
                        // the granularit distance
                        // This is here as I want my length to be 50, but I sometimes swim
                        // 25s (ie 16x25 on :40) but never swim 75, x25, or x75
                        if (numLengths == 1 && (lapDuration / leastCommonLengthTime) < 0.75)
                        {
                            numLengths = 1;
                            leastCommonLength = leastCommonLength / 2;
                        }

                        // calculate the total distance of this swim
                        float lapDistance = (float)((double)numLengths * leastCommonLength);
                        lap.TotalDistanceMeters = (float)Length.Convert(lapDistance, units, Length.Units.Meter);
                    }
                }

                // add the distance track for this lap
                totalDistanceMeters += lap.TotalDistanceMeters;

                if (addDistanceTrack)
                {
                    // need to add the time at the END of the lap as well as the
                    // total distance so far including this lap
                    swimActivity.DistanceMetersTrack.Add(lap.StartTime + lap.TotalTime,
                        (float)totalDistanceMeters);
                    Log.WriteToLogfile("Added: " +
                        swimActivity.DistanceMetersTrack[swimActivity.DistanceMetersTrack.Count - 1].ToString(), Log.LogLevel.DEBUG);
                }
            }

            // set the distance when the user selects "manually entered" for "Data:"
            // if this is calculated, it will use the distance from the sum of track data
            // which will be the same, but will set the Activity duration to not include rest
            swimActivity.TotalDistanceMetersEntered = (float)totalDistanceMeters;

            // use the user setting to determine which is used by default
            // true - lap data is used and activity duration = active lap time + rest lap time
            // false - track data is used and activity duration = total duration - stopped time 
            //         based on "stopped pace" & "include stopped time" category analysis settings
            swimActivity.UseEnteredData = Common.SwimSetSettings.UseTotalDuration;

        }

        private IActivity swimActivity = null;
    }

    class SwimSetAction : IAction
    {

        public SwimSetAction(IDailyActivityView aview, IActivityReportsView rview)
        {
            this.actView = aview;
            this.reportView = rview;
        }

        public SwimSetAction(IActivity activity)
        {
            if (this.activities == null)
            {
                this.activities = new List<IActivity>();
            }

            this.activities.Add(activity);
        }

        #region IAction Members
        public bool Enabled
        {
            get { return (actView != null || reportView != null); }
        }

        public bool HasMenuArrow
        {
            get { return false; }
        }

        public Image Image
        {
            get { return null; }
        }

        public IList<string> MenuPath
        {
            get { return new List<string>(); }
        }

        public void Refresh()
        {
            //throw new NotImplementedException();
        }

        public void Run(Rectangle rectButton)
        {

            //Log.WriteToLogfile("", false);
            foreach (IActivity swimAct in activities)
            {
                SwimSetWorker worker = new SwimSetWorker(swimAct);
                worker.addSwimDistance();
            }

        }

        public string Title
        {
            get { return "Update swim laps"; }
        }

        public bool Visible
        {
            get {
                Boolean isVisible = false;

                foreach (IActivity selectedAct in activities)
                {
                    if (selectedAct != null && selectedAct.Laps.Count > 0)
                    {
                        isVisible = true;
                        break;
                    }
                }

                return isVisible;
            }
        }

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        #endregion


        private IDailyActivityView actView = null;
        private IActivityReportsView reportView = null;

        private IList<IActivity> _activities = null;        
        private IList<IActivity> activities
        {
            get
            {
                //activities are set either directly or by selection,
                //not by more than one view
                if (_activities == null)
                {
                    if (actView != null)
                    {
                        return CollectionUtils.GetAllContainedItemsOfType<IActivity>(actView.SelectionProvider.SelectedItems);
                    }
                    else if (reportView != null)
                    {
                        return CollectionUtils.GetAllContainedItemsOfType<IActivity>(reportView.SelectionProvider.SelectedItems);
                    }
                    else
                    {
                        return new List<IActivity>();
                    }
                }
                return _activities;
            }
            set
            {
                _activities = value;
            }
        } 
    }
}


